package de.koller.aframe.util.noise.n2D;

import de.koller.aframe.util.math.IFunction2D;
import de.koller.aframe.util.misc.BufferedRandomFloat;
import de.koller.oceanFramework.OceanException;
import java.util.Random;

public class Perlin2DQuadTree extends Noise2D {

	private final BufferedRandomFloat crnd;
	public final int posX, posY;
	private final int step;
	
	private float valueMultiplier = 1;
	private float persistence = .5f;
	
	public int steps = 0;
	private Perlin2DQuadTree[][] subTree = new Perlin2DQuadTree[2][2];
	
	public static Perlin2DQuadTree create( BufferedRandomFloat crnd, int x, int y, float persistence, int steps ) {
		Perlin2DQuadTree qt = new Perlin2DQuadTree( crnd, x, y );
		qt.createSubTree( persistence, steps );
		return qt;
	}
	
	public Perlin2DQuadTree( BufferedRandomFloat crnd, int posX, int posY ) {
		this( crnd, posX, posY, 0 );
	}
	
	public Perlin2DQuadTree( BufferedRandomFloat crnd, int posX, int posY, int step ) {
		super( new float[4][4] );
		
		this.step = step;
		this.posX = posX; this.posY = posY;
		this.crnd = crnd;
		
		for( int x = 0; x < 4; x++ )
			for( int y = 0; y < 4; y++ )
				noise[x][y] = crnd.get( posX + x - 1, posY + y - 1, step, 0 );
	}

	public void createSubTree( float persistence ) {
		createSubTree( persistence, -1 );
	}
	
	public void createSubTree( float persistence, int maxStep ) {
		this.steps = maxStep;
		this.persistence = persistence;
		
//		if( subTree != null )
//			throw new OceanException( "subtree already exists!" );
//		
//		subTree = new Perlin2DQuadTree[ 2 ][ 2 ];
//		int px = 2 * posX; int py = 2 * posY;
//		
//		for( int x = 0; x < 2; x++ )
//			for( int y = 0; y < 2; y++ ) {
//				subTree[x][y] = new Perlin2DQuadTree( crnd, px+x, py+y, step + 1 );
//				subTree[x][y].persistence = persistence;
//				if( maxStep > step+1 )
//					subTree[x][y].createSubTree( persistence, maxStep );
//			}
	}
	
	@Override
	public float getValue( float x, float y ) {
		float f = super.getValue( x, y );
		float v = 0;
		
		//if( subTree != null ) {
		if( steps > step ) {
			float dx = 2f * x;
			float dy = 2f * y;

			int ax = (int)Math.floor( dx );
			int ay = (int)Math.floor( dy );
			dx %= 1f;
			dy %= 1f;

			Perlin2DQuadTree qt = subTree[ax][ay];
			if( qt == null ) {
				subTree[ax][ay] = new Perlin2DQuadTree( crnd, posX*2+ax, posY*2+ay, step + 1 );
				subTree[ax][ay].persistence = persistence;
				subTree[ax][ay].steps = steps;
				qt = subTree[ax][ay];
			}
			v = qt.getValue( dx, dy );
		}
			
		return ( f + persistence*v ) * valueMultiplier;
	}
	
	@Override
	public void normalize( float height ) {
		throw new UnsupportedOperationException();
	}

	@Override
	public void normalizeMaxBound( float maxHeight ) {
		valueMultiplier = 1f / maxHeight;
	}
		
	public static void main( String[] args ) {
		BufferedRandomFloat bfr = new BufferedRandomFloat( "./temp/cNoiseTest", new Random() );
		System.out.println( Perlin2DQuadTree.create( bfr, 0, 0, .5f, 100 ).getValue( .3f, .3f ) );
		
//		Noise2D[] p = new Noise2D[4];
//
//		for( int i = 0; i < 4; i++ ) {
//			p[i] = Perlin2DQuadTree.create( bfr,
//					i == 0 || i == 2 ? 2 : 3, 
//					i == 0 || i == 1 ? 2 : 3,
//					.7f, 9 );
//			p[i].normalizeMaxBound( Perlin2D.maxHeight( 0, 9, .7f ) );
//		}
//
//		Function2DPreview.showFunction( 0, 0, 2, 2, new Matchfunction( p ) );
	}
	
	private static class Matchfunction implements IFunction2D {

		private final Noise2D[] perlin;
		
		public Matchfunction( Noise2D[] perlin ) {
			this.perlin = perlin;
		}
		
		@Override
		public float getValue( float x, float y ) {
			if( x >= 1 && y >= 1 ) return perlin[3].getValue( x-1f, y-1f );
			if( x >= 1 && y  < 1 ) return perlin[1].getValue( x-1f, y    );
			if( x <  1 && y >= 1 ) return perlin[2].getValue( x   , y-1f );
			if( x <  1 && y  < 1 ) return perlin[0].getValue( x   , y    );
			throw new OceanException( x + " " + y );
		}
		
	}
	
}
